package cc.unitmesh.agent.tool.impl

import cc.unitmesh.agent.CodingAgentContext
import cc.unitmesh.agent.CodingAgentPromptRenderer
import cc.unitmesh.agent.Platform
import cc.unitmesh.agent.tool.filesystem.FileInfo
import cc.unitmesh.agent.tool.filesystem.ToolFileSystem
import cc.unitmesh.agent.tool.registry.ToolRegistry
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Clock
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

/**
 * Integration tests for WriteFileTool with CodingAgentPromptRenderer
 * Tests the complete workflow from prompt generation to file writing
 */
class WriteFileToolIntegrationTest {
    
    private class MockFileSystem : ToolFileSystem {
        private val files = mutableMapOf<String, String>()
        private val directories = mutableSetOf<String>()
        
        fun getFileContent(path: String): String? = files[path]
        fun getAllFiles(): Map<String, String> = files.toMap()
        
        override fun getProjectPath(): String = "/test-project"
        
        override suspend fun readFile(path: String): String? = files[path]
        
        override suspend fun writeFile(path: String, content: String, createDirectories: Boolean) {
            if (createDirectories) {
                val parentPath = path.substringBeforeLast('/')
                if (parentPath.isNotEmpty() && parentPath != path) {
                    createDirectory(parentPath, true)
                }
            }
            files[path] = content
        }
        
        override fun exists(path: String): Boolean = path in files || path in directories
        
        override fun listFiles(path: String, pattern: String?): List<String> = 
            files.keys.filter { it.startsWith(path) }
        
        override fun resolvePath(relativePath: String): String = 
            if (relativePath.startsWith("/")) relativePath else "/test-project/$relativePath"
        
        override fun getFileInfo(path: String): FileInfo? {
            val content = files[path] ?: return null
            return FileInfo(
                path = path,
                isDirectory = false,
                size = content.length.toLong(),
                lastModified = Clock.System.now().toEpochMilliseconds()
            )
        }
        
        override fun createDirectory(path: String, createParents: Boolean) {
            directories.add(path)
        }
        
        override fun delete(path: String, recursive: Boolean) {
            files.remove(path)
        }
    }
    
    @Test
    fun testPromptGenerationWithWriteFileTool() = runTest {
        val fileSystem = MockFileSystem()
        val toolRegistry = ToolRegistry(fileSystem)
        
        // Create context with tool list
        val context = CodingAgentContext(
            projectPath = "/test-project",
            osInfo = Platform.getOSInfo(),
            timestamp = Platform.getCurrentTimestamp().toString(),
            toolList = toolRegistry.getAgentTools().joinToString("\n\n") { tool ->
                """
                <tool name="${tool.name}">
                  <description>${tool.description}</description>
                  <example>${tool.example}</example>
                </tool>
                """.trimIndent()
            },
            buildTool = "gradle + kotlin",
            shell = Platform.getDefaultShell()
        )
        
        // Generate prompt
        val renderer = CodingAgentPromptRenderer()
        val prompt = renderer.render(context, "EN")
        
        // Verify prompt contains WriteFileTool information
        assertTrue(prompt.contains("write-file"), "Prompt should contain write-file tool")
        assertTrue(prompt.contains("Create new files"), "Prompt should contain tool description")
        assertTrue(prompt.contains("/test-project"), "Prompt should contain project path")
        assertTrue(prompt.contains("gradle + kotlin"), "Prompt should contain build tool")
        
        // Verify tool list formatting
        assertTrue(prompt.contains("<tool name=\"write-file\">"), "Tool should be properly formatted")
    }
    
    @Test
    fun testWriteFileToolWithGeneratedKotlinCode() = runTest {
        val fileSystem = MockFileSystem()
        val tool = WriteFileTool(fileSystem)
        
        // Simulate AI-generated Kotlin code (what a model might produce)
        val generatedKotlinCode = """
            package com.example.service
            
            import kotlinx.coroutines.*
            import kotlinx.serialization.Serializable
            
            /**
             * User service interface for managing user operations
             * Generated by AI coding agent using WriteFileTool
             */
            interface UserService {
                suspend fun createUser(user: User): Result<User>
                suspend fun getUserById(id: String): User?
                suspend fun getAllUsers(): List<User>
                suspend fun updateUser(user: User): Result<User>
                suspend fun deleteUser(id: String): Boolean
            }
            
            /**
             * User data class with validation
             */
            @Serializable
            data class User(
                val id: String,
                val name: String,
                val email: String,
                val createdAt: String
            ) {
                fun isValid(): Boolean {
                    return id.isNotBlank() && 
                           name.isNotBlank() && 
                           email.contains("@") &&
                           createdAt.isNotBlank()
                }
            }
            
            /**
             * In-memory implementation of UserService
             */
            class InMemoryUserService : UserService {
                private val users = mutableMapOf<String, User>()
                
                override suspend fun createUser(user: User): Result<User> {
                    return withContext(Dispatchers.Default) {
                        if (user.isValid()) {
                            users[user.id] = user
                            Result.success(user)
                        } else {
                            Result.failure(IllegalArgumentException("Invalid user"))
                        }
                    }
                }
                
                override suspend fun getUserById(id: String): User? {
                    return users[id]
                }
                
                override suspend fun getAllUsers(): List<User> {
                    return users.values.toList()
                }
                
                override suspend fun updateUser(user: User): Result<User> {
                    return if (users.containsKey(user.id)) {
                        users[user.id] = user
                        Result.success(user)
                    } else {
                        Result.failure(NoSuchElementException("User not found"))
                    }
                }
                
                override suspend fun deleteUser(id: String): Boolean {
                    return users.remove(id) != null
                }
            }
        """.trimIndent()
        
        val params = WriteFileParams(
            path = "src/main/kotlin/com/example/service/UserService.kt",
            content = generatedKotlinCode,
            createDirectories = true
        )
        
        val invocation = tool.createInvocation(params)
        val result = invocation.execute()
        
        assertTrue(result.isSuccess(), "Should successfully write generated code")
        
        val writtenContent = fileSystem.getFileContent("src/main/kotlin/com/example/service/UserService.kt")
        assertEquals(generatedKotlinCode, writtenContent, "Written content should match exactly")
        
        // Verify code structure
        assertTrue(writtenContent!!.contains("package com.example.service"))
        assertTrue(writtenContent.contains("interface UserService"))
        assertTrue(writtenContent.contains("data class User"))
        assertTrue(writtenContent.contains("class InMemoryUserService"))
        assertTrue(writtenContent.contains("suspend fun"))
        assertTrue(writtenContent.contains("withContext(Dispatchers.Default)"))
        
        // Verify line count and formatting
        val lines = writtenContent.lines()
        assertTrue(lines.size > 50, "Should have substantial code content")
        
        // Verify proper indentation is preserved
        val indentedLines = lines.filter { it.startsWith("    ") || it.startsWith("        ") }
        assertTrue(indentedLines.isNotEmpty(), "Should preserve indentation")
        
        println("✅ Successfully wrote ${lines.size} lines of Kotlin code")
        println("📁 File: src/main/kotlin/com/example/service/UserService.kt")
        println("📊 Content length: ${writtenContent.length} characters")
    }
}
